package com.example.myapplication.handler;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.TextView;

import com.example.myapplication.R;

import java.lang.ref.WeakReference;

/**
 *
 * 本周总结：
 * 1、学习handler原理以及使用场景。
 * 2、学习java线程、线程池创建。
 * 3、学习HandlerThread的使用以及使用HandlerThread进行线程间通讯。
 * 4、使用handler实现线程间通信。
 * 5、学习Android网络编程。
 *
 * 主线程不做耗时操作，子线程不更新UI
 *
 * Handler的作用：
 * 1、子线程通知主(ui)线程来更新内容
 * 2、延迟任务
 * 3、Handler的存在就是为了解决在子线程中无法访问UI的问题。(因为Android中的UI控件不是线程安全的)
 *
 * Handler使用场景：
 * 在Android的UI开发中，我们经常会使用Handler来控制主UI程序的界面变化。
 * 我们假设在一个UI界面上面，有一个按钮，当点击这个按钮的时候，会进行网络连接，并把网络上的一个字符串拿下来显示到界面上的一个TextView上面，
 * 这时就出现了一个问题，如果这个网络连接的延迟过大，可能是10秒钟甚至更长，那我们的界面将处于一直假死状态，而如果这段时间超 过5秒钟的话，
 * 程序会出现异常。
 * 这时我们会想到使用线程来完成以上工作，即当按钮被按下的时候新开启一个线程来完成网络连接工作，并把得到的结果更新到UI上面。但是，这时候又会出现另一个问题，
 * 在Android中，主线程是非线程安全的，也就是说UI的更新只能在本线程中完成，其他线程无法直接对主线程进行操作。
 * 为了解决以上问题，Android设计了Handler机制，由Handler来负责与子线程进行通讯，从而让子线程与主线程之间建立起协作的桥梁，使Android的UI更新的问题得到完美的解决。
 * Handler的工作原理：
 *
 * 一般情况下，在主线程中我们绑定了Handler，并在事件触发上面创建新的线程用于完成某些耗时的操作，当子线程中的工作完成之后，
 * 会对Handler发送一个完成的信号，而Handler接收到信号后，就进行主UI界面的更新操作。
 *
 * handler--->主线程(可以切换线程)---》如何通讯（handler如何实现通讯）---》onResume(查)---》 Handler使用场景----》bindService  A  B  C
 */
public class HandlerActivity extends AppCompatActivity {

    private TextView text_view = null;
    private TextView text_view2 = null;
    private String html;
    //使用handler时首先要创建一个handler(普通)
    Handler handler = new Handler();
    // 解决内存泄漏的handler
    Handler myHandler;
    //要用handler来处理多线程可以使用runnable接口，这里先定义该接口
    //线程中运行该接口的run函数
    Runnable update_thread = new Runnable() {
        public void run() {
            //线程每次执行时输出"UpdateThread..."文字,且自动换行
            //textview的append功能和Qt中的append类似，不会覆盖前面
            //的内容，只是Qt中的append默认是自动换行模式
            text_view.append("\n子线程处理完成通知主线程更新。。。");
            //延时1s后又将线程加入到线程队列中
            handler.postDelayed(update_thread, 1000);
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        MyThread myThread = new MyThread();
        myThread.start();
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        text_view = findViewById(R.id.text_view);
        text_view2 = findViewById(R.id.text_view21);
        findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //将线程接口立刻送到线程队列中
                handler.post(update_thread);
            }
        });
        findViewById(R.id.end).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //将接口从线程队列中移除
                handler.removeCallbacks(update_thread);
            }
        });
        findViewById(R.id.post21).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                html = "handler.post()发来的消息-------------》子线程更新主线程的UI";
                                text_view2.setText(html);
                                //postNetRes();
                                Bundle bundle = new Bundle();
                                bundle.putString("data",html);
                                Message message = new Message();
                                message.setData(bundle);
                                handler.sendMessage(message);
                            }
                        });
                    }
                }).start();
            }
        });
        findViewById(R.id.send21).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //handlerThreadSend();
                /*Message message = Message.obtain();
                message.what = 2;
                myHandler.sendMessage(message);*/
                myHandler.obtainMessage(2).sendToTarget();
            }
        });
    }

    /**
     * handler造成内存泄露的原因
     * 在Activity内将Handler声明成非静态内部类或者匿名内部类，这样Handle默认持有外部类Activity的引用。
     * 如果Activity在销毁时，Handler还有未执行完或者正在执行的Message，而Handler又持有Activity的引用，
     * 导致GC无法回收Activity，导致内存泄漏。
     *
     * 为了保证Handler中消息队列中的所有消息都能被执行，此处推荐使用静态内部类 + 弱引用的方式解决内存泄露问题
     *
     * 强引用：生命力最强的引用，只要引用还存在着，垃圾收集器（GC）就不会将该引用给回收，即使会出现内存溢出。
     * 软引用：是用来描述一些有用但是非必须的引用对象，在内存充足的情况下它不会被GC，但是在内存不足时可能会被GC。
     * 弱引用：存活的周期会更短，只要GC执行了，那么被弱引用关联的对象就可能会被回收。弱引用的存活周期可以理解为从创建到下一次GC。
     * 虚引用：是最弱的一种引用，它不会影响对象的生命周期，对象被回收跟它没啥关系。它引用的对象可以在任何时候被回收，
     *       而且也无法根据虚引用来取得一个对象的实例。仅仅当它指向的对象被回收的时候，它会受到一个通知
     *
     */
    static class MyHandler extends Handler{


        private final TextView view;

        public MyHandler(WeakReference<View> v){
            view = v.get().findViewById(R.id.text_view21);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            int i = 1;
            switch (msg.what){
                case 1:
                    view.setText("handlerThread sendMessage1发来的消息-------------》子线程更新主线程的UI");
                    break;
                case 2:
                    view.setText("handlerThread sendMessage2发来的消息-------------》子线程更新主线程的UI");
                    break;
            }
        }
    }

    /**
     * HandlerThread发送消息
     *
     * HandlerThread将loop转到子线程中处理，说白了就是将分担MainLooper的工作量，降低了主线程的压力，使主界面更流畅。
     *
     * 开启一个线程起到多个线程的作用。处理任务是串行执行，按消息发送顺序进行处理。HandlerThread本质是一个线程，在线程内部，代码是串行处理的。
     *
     * 但是由于每一个任务都将以队列的方式逐个被执行到，一旦队列中有某个任务执行时间过长，那么就会导致后续的任务都会被延迟处理。
     *
     * HandlerThread拥有自己的消息队列，它不会干扰或阻塞UI线程。
     *
     * 对于网络IO操作，HandlerThread并不适合，因为它只有一个线程，还得排队一个一个等着。
     */
    public void handlerThreadSend(){
        HandlerThread handlerThread = new HandlerThread("mainThread");
        handlerThread.start();
        myHandler = new MyHandler(new WeakReference<>(getWindow().getDecorView()));
        Message message = Message.obtain();
        message.what = 1;
        myHandler.sendMessage(message);
    }

    /**
     * 不用handlerThread的发送消息的方法
     * Looper:初始化一个MessageQueue和一个本地化变量的Looper，这样handler类就可以通过sendMessage等方法将消息添加到MessageQueue中，
     *        并且通过looper方法将消息不断的取出分发到对应的handler去处理。
     */
    class MyThread extends Thread{
        public void run(){
            /**
             * 调用Looper.prepare()，那么系统就会自动的为该线程建立一个消息队列，然后调用 Looper.loop();
             * 之后就进入了消息循环，这个之后就可以发消息、取消息、和处理消息。
             */
            Looper.prepare();
            myHandler = new Handler(getMainLooper()){
                @Override
                public void handleMessage(@NonNull Message msg) {
                    super.handleMessage(msg);
                    switch (msg.what){
                        case 1:
                            text_view2.setText("handlerThread sendMessage1发来的消息-------------》子线程更新主线程的UI");
                            break;
                        case 2:
                            text_view2.setText("handlerThread sendMessage2发来的消息-------------》子线程更新主线程的UI");
                            break;
                    }
                }
            };
            Looper.loop();
        }
    }

}